﻿using Application_Core.Arbeitsplan_Component.Access_Layer;
using Application_Core.Bauteil_Component.Access_Layer.Exceptions;
using Application_Core.Bauteil_Component.Business_Logic_Layer;
using Application_Core.Bauteil_Component.Data_Access_Layer;
using Application_Core.Bauteil_Component.Data_Access_Layer.Implementations;
using System;
using System.Collections.Generic;
using Utility.Global.Utilities;
using Utility.Persistence_Service;

namespace Application_Core.Bauteil_Component.Access_Layer
{
    public class BauteilComponentFacade : BauteilService, BauteilInternalService
    {
        private readonly BauteilComponentBusinessLogic bauteilLogic;
        private readonly BauteilRepository bauteilRepo;
        private readonly StücklisteRepository stücklisteRepo;
        private readonly StücklistenPositionRepository stücklistenPositionRepo;
        private readonly FertigungsauftragRepository fertigungsauftragRepo;
        private readonly UnitOfWork unitOfWork;
        private readonly ArbeitsplanInternalService arbeitsplanInternal;

        public BauteilComponentFacade(UnitOfWork unitOfWork, ArbeitsplanInternalService arbeitsplanInternal)
        {
            this.unitOfWork = unitOfWork;
            this.arbeitsplanInternal = arbeitsplanInternal;
            this.bauteilRepo = new BauteilRepositoryImpl(unitOfWork);
            this.stücklisteRepo = new StücklisteRepositoryImpl(unitOfWork);
            this.bauteilLogic = new BauteilComponentBusinessLogicImpl(unitOfWork, arbeitsplanInternal);
            this.stücklistenPositionRepo = new StücklistenPositionRepositoryImpl(unitOfWork);
            this.fertigungsauftragRepo = new FertigungsauftragRepositoryImpl(unitOfWork);
        }

        public BauteilComponentFacade(UnitOfWork unitOfWork) : 
            this(unitOfWork, new ArbeitsplanComponentFacade(unitOfWork)) { }

        public TimeSpan FertigeBauteil(BauteilDTO bauteilDTO)
        {
            Check.Argument(bauteilDTO != null, "DataTransferObjekt von Bauteil wird benötigt.");
            Check.Argument(bauteilDTO.Nummer > 0, "Bauteil-Nummer muss positiv sein.");
            Check.Argument(!string.IsNullOrEmpty(bauteilDTO.Name), "Name muss angegeben sein.");

            Bauteil bauteil = this.bauteilRepo.FindByNummer(bauteilDTO.Nummer);
            // Weitergabe an BusinessLogic (Benutzung von ArbeitsplanInternalService)
            return this.bauteilLogic.FertigeBauteil(bauteil);
        }

        public void CreateBauteil(BauteilDTO bauteilDTO)
        {
            Check.Argument(bauteilDTO != null, "DataTransferObjekt von Bauteil wird benötigt.");
            Check.Argument(bauteilDTO.Nummer > 0, "Bauteil-Nummer muss positiv sein.");
            Check.Argument(!string.IsNullOrEmpty(bauteilDTO.Name), "Name muss angegeben sein.");

            Bauteil bauteil = bauteilDTO.ToEntity();
            this.bauteilRepo.Create(bauteil);
        }

        public void CreateStückliste(StücklisteDTO stücklisteDTO, List<StücklistenPositionDTO> positionenDTOs)
        {
            Check.Argument(stücklisteDTO != null, "DataTransferObjekt von Stückliste wird benötigt.");
            Check.Argument(positionenDTOs.Count > 0, "Mindestens eine Position muss in der Stückliste vorhanden sein.");

            // Positionen prüfen und zu Entities umwandeln
            Stückliste stückliste = stücklisteDTO.ToEntity();
            List<StücklistenPosition> positionen = new List<StücklistenPosition>();
            foreach (StücklistenPositionDTO pos in positionenDTOs)
            {
                Check.Argument(pos != null, "DataTransferObjekt von StücklistenPosition wird benötigt (darf nicht null sein).");
                positionen.Add(pos.ToEntity());
            }

            // Save
            Stückliste savedStückliste = this.stücklisteRepo.Create(stückliste);
            foreach (StücklistenPosition pos in positionen)
            {
                pos.Stückliste = savedStückliste;
                this.stücklistenPositionRepo.Create(pos);
            }
        }

        public BauteilDTO FindBauteil(int nummer)
        {
            Check.Argument(nummer > 0, "Bauteil-Nummer muss positiv sein.");

            Bauteil bauteil = this.bauteilRepo.FindByNummer(nummer);
            if (bauteil == null)
            {
                return null;
            }
            return bauteil.ToDTO();
        }

        public List<BauteilDTO> FindBauteile(string name)
        {
            Check.Argument(name != string.Empty, "Name muss angegeben sein.");

            List<Bauteil> bauteile = this.bauteilRepo.FindByName(name);
            List<BauteilDTO> bauteilDTOs = new List<BauteilDTO>();
            foreach (Bauteil bauteil in bauteile)
            {
                bauteilDTOs.Add(bauteil.ToDTO());
            }
            return bauteilDTOs;
        }

        public void DeleteBauteil(int nummer)
        {
            Check.Argument(nummer > 0, "Bauteil-Nummer muss positiv sein.");

            Bauteil bauteil = this.bauteilRepo.FindByNummer(nummer);
            this.bauteilRepo.Delete(bauteil);
        }

        #region Internal

        public int FindIdByNummer(int nummer)
        {
            Check.Argument(nummer > 0, "Bauteilnummer muss positiv sein.");

            int? bauteilIdNullable = this.bauteilRepo.FindIdByNummer(nummer);
            if (bauteilIdNullable == null)
            {
                throw new BauteilNotFoundException("Bauteil mit Nummer " + nummer + " konnte nicht gefunden werden.");
            }
            return (int)bauteilIdNullable;
        }

        public void CreateFertigungsauftrag(int fertigungsauftragsnummer, int auftragId, int bauteilId)
        {
            Bauteil bauteil = this.bauteilRepo.FindById(bauteilId);
            Fertigungsauftrag fertigungsauftrag = new Fertigungsauftrag(fertigungsauftragsnummer, bauteil);
            fertigungsauftrag.AuftragId = auftragId;
            this.fertigungsauftragRepo.Create(fertigungsauftrag);
        }

        public void DeleteFertigungsauftrag(int fertigungsauftragsnummer)
        {
            Check.Argument(fertigungsauftragsnummer > 0, "Fertigungsauftragsnummer muss positiv sein.");

            Fertigungsauftrag fertigungsauftrag = this.fertigungsauftragRepo.FindByNummer(fertigungsauftragsnummer);
            this.fertigungsauftragRepo.Delete(fertigungsauftrag);
        }

        #endregion
    }
}
